home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Collection of Tools & Utilities
/
Collection of Tools and Utilities.iso
/
sound
/
sb01.zip
/
SB.C
< prev
next >
Wrap
C/C++ Source or Header
|
1994-07-11
|
19KB
|
628 lines
/*
* Play and record digitized sound sample on soundblaster DAC/ADC using DMA.
* This source code is in the public domain.
*
* Modification History
*
* 9-Nov-93 David Baggett Wrote it based on Sound Blaster
* <dmb@ai.mit.edu> Freedom project and Linux code.
*
* 24-Jun-94 Gerhard Kordmann - modified for recording facilities
* - added keyboard safety-routines to
* allow stopping of playing/recording
* - removed click while buffer switch
* - added bugfixes by Grzegorz Jablonski
* and several safety checks
* - added free dosmem at end of program
* 08-Jul-94 Gerhard Kordmann - click also removed in recording
* - changes from dosmem... to memcpy
* <kordmann@ldv01.Uni-Trier.de>
* <grzegorz@kmm-lx.p.lod.edu.pl>
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <dos.h>
#include <string.h>
#include <pc.h>
#include "sb.h"
/* GO32 DPMI structs for accessing DOS memory. */
static _go32_dpmi_seginfo dosmem; /* DOS (conventional) memory buffer */
static _go32_dpmi_seginfo oldirq_rm; /* original real mode IRQ */
static _go32_dpmi_registers rm_regs;
static _go32_dpmi_seginfo rm_si; /* real mode interrupt segment info */
static _go32_dpmi_seginfo oldirq_pm; /* original prot-mode IRQ */
static _go32_dpmi_seginfo pm_si; /* prot-mode interrupt segment info */
/* Card parameters */
unsigned int sb_ioaddr;
unsigned int sb_irq;
unsigned int sb_dmachan;
/* Is a sound currently playing or recorded ? */
volatile int sb_dma_active = 0;
/* Conventional memory buffers for DMA. */
static volatile int sb_bufnum = 0;
static char *sb_buf[2];
static unsigned int sb_buflen[2];
/* Info about current sample */
static unsigned char *sb_curdata; /* pointer to next bit of data */
static unsigned long sb_curlength; /* total length length left to play */
/* DMA chunk size, in bytes.
* This parameter determines how big our DMA buffers are. We play
* the sample by piecing together chunks that are this big. This
* means that we don't have to copy the entire sample down into
* conventional memory before playing it. (A nice side effect of
* this is that we can play samples that are longer than 64K.)
*
* Setting this is tricky. If it's too small, we'll get lots
* of interrupts, and slower machines might not be able to keep
* up. Furthermore, the smaller this is, the more grainy the
* sound will come out.
*
* On the other hand, if we make it too big there will be a noticeable
* delay between a call to sb_play and when the sound actually starts
* playing, which is unacceptable for things like games where sound
* effects should be "instantaneous".
*
*/
#define DMA_CHUNK (16000)
/*
* Define replacements for DOS enable and disable.
* Be careful about inlining these -- GCC has a tendency to move
* them around even if you declare them volatile. (This is definitely
* true before 2.5.2; may be fixed in 2.5.2.)
*/
void disable()
{
__asm__ __volatile__ ("cli");
}
void enable()
{
__asm__ __volatile__ ("sti");
}
/* Interrupt handler for recording
*
* This is called in both protected mode and in real mode -- this means
* we don't have to switch modes when we service the interrupt.
*/
void sb_intr_rec(_go32_dpmi_registers *reg)
{
register unsigned n = sb_bufnum; /* buffer we just recorded */
inportb(sb_ioaddr + SB_DSP_DATA_AVAIL); /* Acknowledge soundblaster */
sb_rec_buffer(1 - n); /* Start next buffer recording */
sb_empty_buffer(n); /* Save this buffer */
outportb(0x20, 0x20); /* Acknowledge the interrupt */
enable();
}
/* Save buffer n in sb_curdata and calculate size for next time */
void sb_empty_buffer(register unsigned n)
{
if(sb_buflen[n] > 0) {
memcpy(sb_curdata, (char *)(0xE0000000+(unsigned long) sb_buf[n]), sb_curlength);
/*
* the original version, sometimes spot of misterious crashes
* dosmemget((unsigned long) sb_buf[n], sb_buflen[n], sb_curdata);
*/
sb_curdata += sb_buflen[n];
sb_curlength -= sb_buflen[n];
/* for determination of buffersize keep in mind, that right now */
/* the other buffer is recording up to DMA_CHUNK bytes. Only if */
/* - after saving this other buffer - there is still work to do,*/
/* then this buffer can take the rest in the next round */
if (sb_curlength > DMA_CHUNK) {
if (sb_curlength > 2*DMA_CHUNK)
sb_buflen[n] = DMA_CHUNK;
else
sb_buflen[n] = sb_curlength - DMA_CHUNK;
}
else
sb_buflen[n] = 0;
}
}
void sb_rec_buffer(register unsigned n)
{
int t;
unsigned char im, tm;
if (sb_buflen[n] == 0) { /* See if we're already done */
sb_dma_active = 0;
return;
}
disable();
im = inportb(0x21); /* Enable interrupts on PIC */
tm = ~(1 << sb_irq);
outportb(0x21,im & tm);
outportb(SB_DMA_MASK, 5); /* Set DMA mode to 'record' */
outportb(SB_DMA_FF, 0);
outportb(SB_DMA_MODE, 0x45);
sb_bufnum = n; /* Set transfer address */
t = (int) ((unsigned long) sb_buf[n] >> 16) ;
outportb(SB_DMAPAGE + 3, t & 0xFF);
t = (int) ((unsigned long) sb_buf[n] & 0xFFFF);
outportb(SB_DMA + 2 * sb_dmachan, t & 0xFF);
outportb(SB_DMA + 2 * sb_dmachan, t >> 8);
/* Set transfer length byte count */
outportb(SB_DMA + 2 * sb_dmachan + 1, (sb_buflen[n]-1) & 0xFF);
outportb(SB_DMA + 2 * sb_dmachan + 1, (sb_buflen[n]-1) >> 8);
outportb(SB_DMA_MASK, sb_dmachan); /* Unmask DMA channel */
enable();
sb_writedac(SB_DMA_ADC); /* command byte for DMA ADC transfer */
sb_writedac((sb_buflen[n]-1) & 0xFF); /* sb_write length */
sb_writedac((sb_buflen[n]-1) >> 8);
/* A sound is recorded now. */
sb_dma_active = 1; /* A sound is recorded now. */
}
/* Record a sample through the ADC using DMA. */
/* return number of bytes actually recorded */
unsigned long sb_rec(unsigned char *data, unsigned long length)
{
sb_install_interrupts(sb_intr_rec); /* Install our interrupt handlers */
sb_curdata = data; /* Prime the buffers */
sb_curlength = length;
if(length > DMA_CHUNK)
sb_buflen[0] = DMA_CHUNK;
else
sb_buflen[0] = length;
if(length <= DMA_CHUNK)
sb_buflen[1] = 0;
else
if(length > 2*DMA_CHUNK)
sb_buflen[1] = DMA_CHUNK;
else
sb_buflen[1] = length-DMA_CHUNK;
sb_rec_buffer(0); /* Start the first buffer recording. */
while (sb_dma_active)
if(mykbhit()) { /* kbhit crashed sometimes */
int rest;
rest = sb_read_counter(); /* samples still to record */
sb_writedac(SB_HALT_DMA); /* stop playing */
rest = sb_buflen[sb_bufnum] - rest;/* samples already recorded */
length -= (sb_curlength - rest);/* total samples recorded */
sb_buflen[sb_bufnum] = rest; /* save those samples */
sb_empty_buffer(sb_bufnum);
sb_dma_active = 0; /* and exit the loop */
sb_buflen[0] = sb_buflen[1] = sb_curlength = 0; /* clean up */
}
sb_cleanup_ints(); /* remove interrupts */
return length;
}
void sb_intr_play(_go32_dpmi_registers *reg)
{
register unsigned n = sb_bufnum; /* buffer we just played */
inportb(sb_ioaddr + SB_DSP_DATA_AVAIL); /* Acknowledge soundblaster */
sb_play_buffer(1 - n); /* Start next buffer player */
sb_fill_buffer(n);